using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using MediatR;
using TerritoryGame.API.Hubs;
using TerritoryGame.Application.Commands;
using TerritoryGame.Application.Queries;
using TerritoryGame.Domain.Repositories;
using TerritoryGame.Infrastructure.Data;
using TerritoryGame.Infrastructure.Repositories;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Configure HTTPS ports
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(5000); // HTTP
    options.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.UseHttps(); // HTTPS with default development certificate
    });
});

// Configure HTTPS redirection
builder.Services.AddHttpsRedirection(options =>
{
    options.HttpsPort = 5001;
    options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
});

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Territory Game API", Version = "v1" });
});

// Add SignalR
builder.Services.AddSignalR();

// Add MediatR for CQRS
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateGameCommand).Assembly));

// Add Entity Framework Core with retry logic
builder.Services.AddDbContext<TerritoryGameDbContext>(options =>
{
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    options.UseNpgsql(connectionString, npgsqlOptions =>
    {
        npgsqlOptions.EnableRetryOnFailure(
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(5),
            errorCodesToAdd: null);
    });
});

// Add Redis (optional - only if connection string is available)
var redisConnectionString = builder.Configuration.GetConnectionString("Redis");
if (!string.IsNullOrEmpty(redisConnectionString))
{
    builder.Services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = redisConnectionString;
    });
}

// Add Repositories
builder.Services.AddScoped<IGameRepository, GameRepository>();

// Add CORS
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.SetIsOriginAllowed(origin => true) // 允许所有来源
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials(); // 允许凭据，SignalR需要
    });
});

// JWT Authentication
var jwtKey = builder.Configuration["Jwt:Key"] ?? "Dev_Temp_Key_Change_Me_This_Is_At_Least_64_Characters_Long_0123456789";
var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "TerritoryGame";
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.RequireHttpsMetadata = false;
    options.SaveToken = true;
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
        ValidateIssuer = true,
        ValidateAudience = false,
        ValidIssuer = jwtIssuer
    };
    // 允许从 SignalR 的查询字符串读取访问令牌
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/gamehub"))
            {
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});

var app = builder.Build();

// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// CORS必须在其他中间件之前应用
app.UseCors("AllowAll");

// 临时禁用HTTPS重定向以解决SignalR协商问题
// app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();

// Map SignalR Hub
app.MapHub<GameHub>("/gamehub");

// 添加健康检查端点
app.MapGet("/health", () => Results.Ok("OK"));

// Map Minimal API endpoints
app.MapPost("/api/games", async (CreateGameCommand command, IMediator mediator) =>
{
    var result = await mediator.Send(command);
    return Results.Ok(result);
})
.WithName("CreateGame")
.RequireAuthorization()
.WithOpenApi();

app.MapPost("/api/games/join", async (JoinGameCommand command, IMediator mediator) =>
{
    var result = await mediator.Send(command);
    return Results.Ok(result);
})
.WithName("JoinGame")
.RequireAuthorization()
.WithOpenApi();

app.MapPost("/api/games/paint", async (PaintCommand command, IMediator mediator) =>
{
    var result = await mediator.Send(command);
    return Results.Ok(result);
})
.WithName("Paint")
.RequireAuthorization()
.WithOpenApi();

app.MapGet("/api/games/{roomCode}", async (string roomCode, IMediator mediator) =>
{
    var query = new GetGameQuery { RoomCode = roomCode };
    var result = await mediator.Send(query);
    return result != null ? Results.Ok(result) : Results.NotFound();
})
.WithName("GetGame")
.RequireAuthorization()
.WithOpenApi();

app.MapControllers();

// Ensure database is migrated with retry logic
using (var scope = app.Services.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<TerritoryGameDbContext>();
    var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
    
    var maxRetries = 5;
    var retryDelay = TimeSpan.FromSeconds(2);
    
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            logger.LogInformation("Attempting to connect to database (attempt {Attempt}/{MaxAttempts})", i + 1, maxRetries);
            await context.Database.MigrateAsync();
            logger.LogInformation("Database connection successful");
            break;
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Database connection attempt {Attempt} failed", i + 1);
            
            if (i == maxRetries - 1)
            {
                logger.LogError(ex, "Failed to connect to database after {MaxAttempts} attempts", maxRetries);
                throw;
            }
            
            await Task.Delay(retryDelay);
            retryDelay = TimeSpan.FromSeconds(Math.Min(retryDelay.TotalSeconds * 2, 30)); // Exponential backoff
        }
    }
}

// Minimal API: 历史记录查询
app.MapGet("/api/history", async (TerritoryGameDbContext db, HttpContext http) =>
{
    var userId = http.User?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
                 ?? http.User?.FindFirst(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub)?.Value;
    
    if (string.IsNullOrEmpty(userId))
        return Results.Unauthorized();
    
    // 先获取用户参与的游戏历史ID列表
    var userGameHistoryIds = await db.GameHistoryPlayers
        .Where(p => p.UserId == userId)
        .Select(p => p.GameHistoryId)
        .Distinct()
        .ToListAsync();
    
    // 根据ID列表获取游戏历史记录
    var list = await db.GameHistories
        .Where(h => userGameHistoryIds.Contains(h.Id))
        .OrderByDescending(h => h.EndedAt)
        .Select(h => new {
            h.Id,
            h.RoomCode,
            h.RoomName,
            h.EndedAt,
            h.DurationSeconds,
            h.WinnerPlayerId,
            h.WinnerNickname,
            h.WinnerColor
        })
        .ToListAsync();
    
    return Results.Ok(list);
}).WithName("GetHistory").RequireAuthorization().WithOpenApi();

// 实时排行榜API（支持 type=best|total 与 top 参数）
app.MapGet("/api/leaderboard", async (TerritoryGameDbContext db, HttpRequest request) =>
{
    try
    {
        var type = request.Query["type"].ToString()?.ToLowerInvariant();
        if (string.IsNullOrWhiteSpace(type)) type = "best"; // 默认最佳榜
        var topParam = request.Query["top"].ToString();
        int top = 5;
        if (!string.IsNullOrWhiteSpace(topParam) && int.TryParse(topParam, out var t) && t > 0 && t <= 50)
        {
            top = t;
        }

        // 获取所有玩家的面积数据
        var leaderboard = await db.GameHistoryPlayers
            .Where(p => p.Area > 0) // 只统计有面积的玩家
            .GroupBy(p => new { p.UserId, p.Nickname, p.Color })
            .Select(g => new
            {
                UserId = g.Key.UserId,
                Nickname = g.Key.Nickname,
                Color = g.Key.Color,
                TotalArea = g.Sum(p => p.Area), // 累计面积
                GameCount = g.Count(), // 参与游戏次数
                BestArea = g.Max(p => p.Area) // 单局最佳面积
            })
            .OrderByDescending(x => type == "total" ? x.TotalArea : x.BestArea)
            .Take(top)
            .ToListAsync();

        // 添加排名
        var rankedLeaderboard = leaderboard.Select((player, index) => new
        {
            Rank = index + 1,
            player.UserId,
            player.Nickname,
            player.Color,
            player.TotalArea,
            player.GameCount,
            player.BestArea
        }).ToList();

        return Results.Ok(rankedLeaderboard);
    }
    catch (Exception ex)
    {
        return Results.Problem($"获取排行榜失败: {ex.Message}");
    }
}).WithName("GetLeaderboard").WithOpenApi();

app.MapGet("/api/history/{id:guid}", async (Guid id, TerritoryGameDbContext db, HttpContext http) =>
{
    var userId = http.User?.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value
                 ?? http.User?.FindFirst(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub)?.Value;
    var history = await db.GameHistories
        .Where(h => h.Id == id)
        .Select(h => new {
            h.Id,
            h.RoomCode,
            h.RoomName,
            h.EndedAt,
            h.DurationSeconds,
            h.WinnerPlayerId,
            h.WinnerNickname,
            h.WinnerColor,
            Players = db.GameHistoryPlayers
                .Where(p => p.GameHistoryId == h.Id)
                .OrderBy(p => p.Rank)
                .Select(p => new { p.PlayerId, p.Nickname, p.Color, p.Area, p.Rank })
                .ToList(),
            HasMe = db.GameHistoryPlayers.Any(p => p.GameHistoryId == h.Id && p.UserId == userId)
        })
        .FirstOrDefaultAsync();

    if (history == null || !history.HasMe)
        return Results.NotFound();

    return Results.Ok(new {
        history.Id,
        history.RoomCode,
        history.RoomName,
        history.EndedAt,
        history.DurationSeconds,
        history.WinnerPlayerId,
        history.WinnerNickname,
        history.WinnerColor,
        history.Players
    });
}).WithName("GetHistoryById").RequireAuthorization().WithOpenApi();

app.Run();
